Introduction to Win32 Files

Like DOS files, Win32 files are treated as byte "addressable" data. The four fundamental file functions are

CreateFile
ReadFile
WriteFile
CloseHandle

The functions ReadFile and WriteFile do not have A and W versions because the data can be any kind of binary data. However, CreateFile does have two versions because the file name can be either an ANSI string or a Unicode string. Win95 supports only the ANSI version.

A simple (?!) file viewer, WINVIEW.ASM

The following implements a simple file viewer. A popup window is created for getting the name of the file we want to view. An OK button causes the named file to appear in the main window.

The program uses one overlapped window, one popup window, and three child windows. We assign a "control ID" for each of the child windows, although we'll only refer to only one of them explicitly.

ID_mainedit     equ     101
ID_popupedit    equ     102
ID_popupbtn     equ     103
The child windows, or controls, have predefined classes. That leaves us with two windows to define classes for. The winmain module will register one window class. We'll need to register the other one in our initialization routine.
    The main window is not made visible, so that the popup (which is visible) will be the first window we see from this program. The popup will change the visibility status of the main window.
        .data
        ; main window
wc WNDCLASSEX <size WNDCLASSEX,CS_HREDRAW+CS_VREDRAW,WndProc,0,0, 0, \
                        0,0,COLOR_WINDOW+1, 0,wndclsname,0>
wndmain CREATEARGS <0,wndclsname,caption,WS_OVERLAPPEDWINDOW,\
                        100,50,400,400, 0,0,0,0>
wndedit CREATEARGS <0,editclsname,0,WS_CHILD+WS_HSCROLL+WS_VSCROLL+\
                        WS_VISIBLE+ES_AUTOHSCROLL+ES_AUTOVSCROLL+ES_MULTILINE,\
                        0,0,0,0, 0,ID_mainedit,0,0>
        ; popup window
wc_popup WNDCLASSEX <size WNDCLASSEX,0,PopUpWndProc,0,0, 0,\
                        0,0,COLOR_3DFACE+1, 0,popupname,0>
wndpopup CREATEARGS <0,popupname,0,WS_POPUP+WS_BORDER+WS_VISIBLE, \
                        100,100,200,50, 0,0,0,0>
wndfname CREATEARGS <0,editclsname,0,WS_CHILD+WS_VISIBLE+ES_AUTOHSCROLL,\
                        0,0,200,20, 0,ID_popupedit,0,0>
wndbtn   CREATEARGS <0,btnclsname,btn_text,WS_CHILD+WS_VISIBLE,\
                        0,25,40,21, 0,ID_popupbtn,0,0>

hMain dd 0
hEdit dd 0
hFnameEdit dd 0

wndclsname      db 'FileViewer',0
popupname       db 'Popup',0
editclsname     db 'edit',0
btnclsname      db 'button',0

caption db 'File Viewer',0
btn_text        db 'OK',0

        public  InitApp,EndApp

        extrn   LoadCursor:near
        extrn   GetModuleHandle:near
        extrn   RegisterClassEx:near
;
        .code
InitApp:
        mov     esi,offset wc_popup
        mov     edi,offset wc

        push    large 0
        call    GetModuleHandle ; get program instance
        mov     [esi].wcxInstance,eax

        push    large IDC_ARROW
        push    large 0
        call    LoadCursor
        mov     [esi].wcxCursor,eax     ; to wc_popup
        mov     [edi].wcxCursor,eax     ; to wc (main)

        push    esi
        call    RegisterClassEx

        mov     esi,offset wndmain

        ret
;
EndApp:
        xor     eax,eax ; assume no errors
        ret
The WM_CREATE handler is the only real difference between the main windows in this program and in our scratchpad editor. We save the main window handle, and create the popup window.
    We don't have the popup owned by the main window because we want to hide the main window and leave the popup on display. We don't have the main window owned by the popup because we want to destroy the popup after we've retrieved our file.
        extrn   CreateWindowEx:near

finish_create:
        mov     eax,[esp+4+0]   ; grab hwnd before ESP changes
        mov     [hMain],eax     ; save for popup

        push    esi
        push    edi

        mov     esi,offset wndedit
        mov     [esi].cwargParent,eax    ; make "wndmain" parent of "edit"
        mov     eax,[wc].wcxInstance
        mov     [esi].cwargInstance,eax  ; set edit instance
        sub     esp,48    ; allocate args
        mov     edi,esp
        mov     ecx,12
        rep movsd
        call    CreateWindowEx
        mov     [hEdit],eax              ; save edit window handle

        mov     esi,offset wndpopup
        mov     eax,[wc].wcxInstance
        mov     [esi].cwargInstance,eax  ; set popup instance
        sub     esp,48    ; allocate args
        mov     edi,esp
        mov     ecx,12
        rep movsd
        call    CreateWindowEx

        pop     edi
        pop     esi

        xor     eax,eax    ; signal a successful CREATE
        ret     16
The popup window is where most of our custom work appears. The button control, as a child of the popup, sends a WM_COMMAND to the popup when clicked. So we need to handle this extra message in our popup.
        .code
PopUpWndProc:
        mov     eax,[esp+4+4]           ; message ID
        cmp     eax,WM_CREATE           ; window created
        je      finish_create_popup
        cmp     eax,WM_COMMAND
        je      do_command_popup
        cmp     eax,WM_DESTROY          ; about to start window destruction
        je      start_destroy_popup
        jmp     DefWindowProc           ; delegate other message processing
When we create the popup, we also create two controls and establish the popup as their parent. We save the edit control handle, so that we can retrieve the text that was entered there.
        .code
finish_create_popup:
        mov     eax,[esp+4+0]  ; grab hwnd before ESP changes

        push    esi
        push    edi

        mov     esi,offset wndfname
        mov     [esi].cwargParent,eax    ; make "popup" parent of "edit"
        mov     eax,[wc].wcxInstance
        mov     [esi].cwargInstance,eax  ; set edit instance
        sub     esp,48    ; allocate args
        mov     edi,esp
        mov     ecx,12
        rep movsd
        call    CreateWindowEx
        mov     [hFnameEdit],eax

        mov     esi,offset wndbtn
        mov     eax,[wndfname].cwargParent
        mov     [esi].cwargParent,eax    ; make "popup" parent of "button"
        mov     eax,[wc].wcxInstance
        mov     [esi].cwargInstance,eax  ; set button instance
        sub     esp,48    ; allocate args
        mov     edi,esp
        mov     ecx,12
        rep movsd
        call    CreateWindowEx

        pop     edi
        pop     esi

        xor     eax,eax    ; signal a successful CREATE
        ret     16
Like the arguments for CreateWindowEx, we create an argument list data structure for CreateFile. We also set aside some storage for the file name and file data.
CREATEFILEARGS STRUC
cfargFileName   dd 0
cfargDesiredAccess dd 0
cfargShareMode  dd 0
cfargSecurityAttr       dd 0
cfargDistribution       dd 0
cfargFlagsAndAttrs dd 0
cfargTemplateFile       dd 0
CREATEFILEARGS ENDS

        .data
        ; "create" file arguments
text_file CREATEFILEARGS <fname,GENERIC_READ,0,0,OPEN_EXISTING,\
                                        FILE_ATTRIBUTE_NORMAL,0>
bytes_transferred dd 0

fname           db 256 dup(0)

        .data?
fbuffer db 33000 dup(?)
MAXBUF  equ 32000               ; Win95 EDIT control is 16-bit
The only popup message we handle is the button click message. We do everything here except browse and terminate the program. Before exiting, we tell the popup window to destroy itself.
    This is one way of doing this. The more conventional method is to use a dialog box. The common dialog known as the Open File dialog is even more convenient because it can extract some data (e.g., the file name) for you.
        extrn   ShowWindow:near,GetWindowText:near,DestroyWindow:near
        extrn   CreateFile:near,ReadFile:near,CloseHandle:near
        extrn   SetWindowText:near

        .code
do_command_popup:
        mov     eax,[esp+4+8]   ; third arg is wParam
        cmp     eax,(BN_CLICKED shl 16)+ID_popupbtn
        je      button_click
        xor     eax,eax
        ret     16

button_click:
        push    esi
        push    edi

        push    large 255
        push    offset fname
        push    [hFnameEdit]
        call    GetWindowText   ; get file name from edit window

        mov     esi,offset text_file
        sub     esp,28          ; allocate args
        mov     edi,esp
        mov     ecx,7
        rep movsd
        call    CreateFile
        cmp     eax,INVALID_HANDLE_VALUE
        je      no_file

        push    eax             ; push file handle for CloseHandle

        push    large 0 ; no overlap structure
        push    offset bytes_transferred
        push    large MAXBUF
        push    offset fbuffer
        push    eax             ; file handle
        call    ReadFile

        call    CloseHandle

        mov     ecx,bytes_transferred
        mov     fbuffer[ecx],0  ; terminate data

        push    offset fbuffer
        push    [hEdit]
        call    SetWindowText

no_file:
        ; restore esp (as well as edi and esi)
        pop     edi
        pop     esi

        push    dword ptr [esp+4+0]     ; hwnd of this window
        call    DestroyWindow           ; this window is no longer needed

        xor     eax,eax
        ret     16
We bring up the main window with this message, instead of in WM_COMMAND. This is in case the popup window was removed by another means (e.g., Alt-F4).
start_destroy_popup:
        push    large SW_RESTORE
        push    [hMain]
        call    ShowWindow

        xor     eax,eax
        ret     16